/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System Kernel Module                                  */
/*                                                                           */
/*    Copyright (C) 2008 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------     */
/*    Module: krnlUART - UART Kernel Driver (line oriented, text mode)       */
/*                                                                           */
/*    drvIOconfig declarations:                                              */
/*      UART_RECEIVE_BUFFER_LENGTH = Receive buffer lenth [byte]             */
/*      UART_TRANSMIT_BUFFER_LENGTH = Transmit buffer length [byte]          */
/*      UART_MESSAGE_ENABLED = Defined if UART must send message             */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Includes
#include <krnlTypes.h>
#include "drvIOConfig.h"
#include <drvUART.h>
#include <krnlString.h>
#include <krnlAsciiCodes.h>
#include <krnlUART.h>

#ifdef UART_MESSAGE_ENABLED
#include "krnlMessages.h"
#include <krnlMessageQueue.h>
#endif

///////////////////////////////////////////////////////////////////////////////
// Module global variables
 dosChar g_receive_buffer[UART_RECEIVE_BUFFER_LENGTH];
static dosByte g_receive_buffer_pointer = 0;
static dosBool g_line_received = 0;

static dosChar g_transmit_buffer[UART_TRANSMIT_BUFFER_LENGTH];
static dosByte g_transmit_pop_pointer = 0;
static dosByte g_transmit_push_pointer = 0;

///////////////////////////////////////////////////////////////////////////////
// UART Initialize
void krnlUARTInit(void)
{
	drvUARTInit();
}	

/*****************************************************************************/
/* Receiver section                                                          */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Character received interrupt callback
void krnlUARTReceiveInterrupt( dosChar in_char )
{
	// check received character
	switch( in_char )
	{
		// handle backspace
		case ASCII_BS:
			if( g_receive_buffer_pointer > 0 )
				g_receive_buffer_pointer--;
			break;
		
		// handle line end (CR)
		case ASCII_CR:
			if( !g_line_received )
			{
				g_receive_buffer[g_receive_buffer_pointer] = ASCII_NUL;
				g_line_received = dosTrue;
			
			// send message if required
#ifdef UART_MESSAGE_ENABLED
				{
					krnlMessage* message;
				
					message = krnlAllocateAsyncMessage();
					
					if( message != DOS_NULL )
						message->Id = msgUART_LINE_RECEIVED;
				}
#endif
			}
			break;
			
		// skip LF
		case ASCII_LF:
			break;
			
		// store character
		default:
			if( g_receive_buffer_pointer < UART_RECEIVE_BUFFER_LENGTH - 1 )
				g_receive_buffer[g_receive_buffer_pointer++] = in_char;
			break;
	}
}	

///////////////////////////////////////////////////////////////////////////////
// Gets received line length
dosByte krnlUARTGetReceivedLineLength()
{
	return g_receive_buffer_pointer;
}

///////////////////////////////////////////////////////////////////////////////
// Restart receiver
void krnlUARTRestartReceiver(void)
{
	g_receive_buffer_pointer = 0;
	g_line_received = dosFalse;
}	

///////////////////////////////////////////////////////////////////////////////
// Get Character from buffer
dosChar krnlUARTGetCharacter(dosByte in_index)
{
  if( in_index < UART_RECEIVE_BUFFER_LENGTH - 1 )
	  return g_receive_buffer[in_index];
	else
	  return '\0';
}  

///////////////////////////////////////////////////////////////////////////////
// True if line is received
dosBool krnlUARTIsLineReceived(void)
{
	return g_line_received;
}

///////////////////////////////////////////////////////////////////////////////
// Parser routines

///////////////////////////////////////////////////////////////////////////////
// Skip Whitespaces
void krnlUARTSkipWhitespaces(dosByte* in_index)
{
	strSkipWhitespaces( g_receive_buffer, in_index );
}
	
///////////////////////////////////////////////////////////////////////////////
// Check character
void krnlUARTCheckForCharacter(dosByte* in_index, dosBool* in_success, dosChar in_char )
{
	if( *in_success )
	{
		if( g_receive_buffer[*in_index] != in_char )
			*in_success = dosFalse;
		else
			(*in_index)++;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Check for const string
dosBool krnlUARTCheckForConstStringNoCase( dosByte* in_index, dosConstString in_string )
{
	return strCheckForConstStringNoCase(g_receive_buffer, UART_RECEIVE_BUFFER_LENGTH, in_index, in_string );
}	

///////////////////////////////////////////////////////////////////////////////
// Char to int16
void krnlUARTCharToInt16( dosByte* in_index, dosBool* in_success, dosInt16* out_number )
{
	strStringToInt16( g_receive_buffer, in_index, in_success, out_number );
}	
/*****************************************************************************/
/* Transmitter section                                                       */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// True if transmitter buffer is empty
dosBool krnlUARTIsTransmitterEmpty(void)
{
	return (g_transmit_pop_pointer == g_transmit_push_pointer);
}

///////////////////////////////////////////////////////////////////////////////
// UART Transmit complete interrupt
void krnlUARTTransmitInterrupt(void)
{
	// increment pointer
	g_transmit_pop_pointer++;
	
	if( g_transmit_pop_pointer >= UART_TRANSMIT_BUFFER_LENGTH )
		g_transmit_pop_pointer = 0;
		
	// send character
	drvUARTSendCharacter( g_transmit_buffer[g_transmit_pop_pointer] );
	
	// disable interrupt if there is no more character to send		
	if( g_transmit_pop_pointer == g_transmit_push_pointer )
	{
		drvUARTDisableTransmitInterrupt();
  }
}	

///////////////////////////////////////////////////////////////////////////////
// Send constant string
void krnlUARTSendConstString( dosConstString in_string )
{
	while( *in_string != '\0' )
	{
		krnlUARTSendCharacter( *in_string );
		in_string++;
	}
}	

///////////////////////////////////////////////////////////////////////////////
// Send string
dosByte krnlUARTSendString( dosString in_string )
{
	dosByte index = 0;
	
	while( in_string[index] != '\0' )
	{
		krnlUARTSendCharacter( in_string[index++] );
	}
	
	return dosTrue;
}	

///////////////////////////////////////////////////////////////////////////////
// Send character
void krnlUARTSendCharacter( dosChar in_char )
{
	dosByte next_push_pointer = g_transmit_push_pointer + 1;
	
	if( next_push_pointer >= UART_TRANSMIT_BUFFER_LENGTH )
		next_push_pointer = 0;
		
	// wait if buffer is full
	while( next_push_pointer == g_transmit_pop_pointer );
		
	// store character in buffer
	g_transmit_buffer[next_push_pointer] = in_char;
	
	g_transmit_push_pointer = next_push_pointer;
	
	drvUARTEnableTransmitInterrupt();
}
